﻿// -----------------------------------------------------------------------
// <copyright file="NoiseAdded.cs" company="fcis">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace LastVersionOfIpPackage.Operations
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class NoiseAdded 
    {
        /// <summary>
        /// static object of this class to use this without create object
        /// </summary>
        private static NoiseAdded noiseAdded = new NoiseAdded();

        /// <summary>
        /// buffer of image
        /// </summary>
        private Color[,] bufferOfImage;

        /// <summary>
        /// value Of Image Plus Noise Value
        /// </summary>
        private double[,,] valueOfImagePlusNoiseValue;

        /// <summary>
        /// Gets or sets the noise added1.
        /// </summary>
        /// <value>
        /// The noise added1.
        /// </value>
        public static NoiseAdded NoiseAdded1
        {
            get { return NoiseAdded.noiseAdded; }
            set { NoiseAdded.noiseAdded = value; }
        }

        /// <summary>
        /// Adds the salt and pepper noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="probOfSalt">The prob of salt happened.</param>
        /// <param name="probOfPepper">The prob of pepper happend.</param>
        /// <returns>
        /// new image
        /// </returns>
        public Bitmap AddSaltAndPepperNoise(Bitmap image, double probOfSalt, double probOfPepper)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
           Bitmap newImag = new Bitmap(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
           Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
           int numberOfPixelSalt = (int)Math.Floor((double)this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * probOfSalt);
            this.ChangeValueOfBuffer(numberOfPixelSalt, 255);
            int numberOfPixelPepper = (int)Math.Floor((double)this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * probOfPepper);
            this.ChangeValueOfBuffer(numberOfPixelPepper, 0);
            for (int j = 0; j < this.bufferOfImage.GetLength(0); j++)
            {
                for (int k = 0; k < this.bufferOfImage.GetLength(1); k++)
                {
                    newImag.SetPixel(j, k, this.bufferOfImage[j, k]);
                }
            }

                return newImag;
        }

        /// <summary>
        /// Adds the uniform noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">start of uniform noise value.</param>
        /// <param name="b">end of uniform noise value.</param>
        /// <param name="precentage">The precentage of noise.</param>
        /// <returns>
        /// the new image
        /// </returns>
        public Bitmap AddUniformNoise(Bitmap image, int a, int b, double precentage)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            int numberOfNoisePixel = (int)Math.Floor((double)(1.0 / (b - a)) * this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * precentage);
            this.ChangeValueOfBuffer(numberOfNoisePixel, a, b);
            this.bufferOfImage = Normalize.Normalize11.Normalization(this.valueOfImagePlusNoiseValue);
          return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Adds the gaussian noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="mean">The mean of noise.</param>
        /// <param name="sigma">The sigma of noise.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        /// <returns>
        /// new image
        /// </returns>
        public Bitmap AddGaussianNoise(Bitmap image, double mean, double sigma, double precentage)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            this.ChangeValueOfBuffer(mean, sigma, precentage);
            this.bufferOfImage = Normalize.Normalize11.Normalization(this.valueOfImagePlusNoiseValue);
            return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Adds the gaussian noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">the A.</param>
        /// <param name="b">The b.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        /// <returns>
        /// new image
        /// </returns>
        public Bitmap AddRayleighNoise(Bitmap image, double a, double b, double precentage)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            int allNumberOfPixelChange = 0;
            Random newRand = new Random();
            Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            for (int i = 0; i < 256; i++)
            {
                int numberOfPixel;
                if (i >= a)
                {
                    numberOfPixel = (int)((2 / b) * (i - a) * Math.Exp(-Math.Pow(i - a, 2) / b) * this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * precentage);
                }
                else
                {
                    numberOfPixel = 0;
                }

                for (int j = 0; j < numberOfPixel; j++)
                {
                    Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.bufferOfImage[randomValue.X, randomValue.Y].R + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.bufferOfImage[randomValue.X, randomValue.Y].G + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.bufferOfImage[randomValue.X, randomValue.Y].B + i;
                }

                allNumberOfPixelChange += numberOfPixel;
            }

            for (int i = 0; i < (this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1)) - allNumberOfPixelChange; i++)
            {
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 0] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 1] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 2] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].B;
            }

            this.bufferOfImage = Normalize.Normalize11.Normalization(this.valueOfImagePlusNoiseValue);
            return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Adds the Exponential noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="landa">The landa.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        /// <returns>
        /// new image
        /// </returns>
        public Bitmap AddExponentialNoise(Bitmap image, double landa, double precentage)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            int allNumberOfPixelChange = 0;
            Random newRand = new Random();
            Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            for (int i = 0; i < 256; i++)
            {
                int numberOfPixel = (int)(landa * Math.Exp(-landa * i) * this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * precentage);

                for (int j = 0; j < numberOfPixel; j++)
                {
                    Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.bufferOfImage[randomValue.X, randomValue.Y].R + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.bufferOfImage[randomValue.X, randomValue.Y].G + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.bufferOfImage[randomValue.X, randomValue.Y].B + i;
                }

                allNumberOfPixelChange += numberOfPixel;
            }

            for (int i = 0; i < (this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1)) - allNumberOfPixelChange; i++)
            {
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 0] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 1] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 2] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].B;
            }

            this.bufferOfImage = Normalize.Normalize11.Normalization(this.valueOfImagePlusNoiseValue);
            return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Adds the gaussian noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">the A.</param>
        /// <param name="b">The b.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        /// <returns>
        /// new image
        /// </returns>
        public Bitmap AddGammaNoise(Bitmap image, double a, int b, double precentage)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            int allNumberOfPixelChange = 0;
            Random newRand = new Random();
            Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            for (int i = 0; i < 256; i++)
            {
                int numberOfPixel = (int)((Math.Pow(a, b) * Math.Pow(i, b - 1) * Math.Exp(-a * i) / this.Fact(b - 1)) * this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * precentage);

                for (int j = 0; j < numberOfPixel; j++)
                {
                    Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.bufferOfImage[randomValue.X, randomValue.Y].R + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.bufferOfImage[randomValue.X, randomValue.Y].G + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.bufferOfImage[randomValue.X, randomValue.Y].B + i;
                }

                allNumberOfPixelChange += numberOfPixel;
            }

            for (int i = 0; i < (this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1)) - allNumberOfPixelChange; i++)
            {
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 0] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 1] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 2] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].B;
            }

            this.bufferOfImage = Normalize.Normalize11.Normalization(this.valueOfImagePlusNoiseValue);
            return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Adds the periodic noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="ampitude">The ampitude.</param>
        /// <param name="freX">The fre X.</param>
        /// <param name="freY">The fre Y.</param>
        /// <param name="phX">The ph X.</param>
        /// <param name="phY">The ph Y.</param>
        /// <returns>
        /// noise image
        /// </returns>
        public Bitmap AddPeriodicNoise(Bitmap image, double ampitude, double freX, double freY, double phX, double phY)
        {
            this.bufferOfImage = BufferOfImage.Buffer.GetBuffer(image);
            double[,,] valueOFImageWithNoise = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            for (int i = 0; i < this.bufferOfImage.GetLength(1); i++)
            {
                for (int j = 0; j < this.bufferOfImage.GetLength(0); j++)
                {
                    valueOFImageWithNoise[j, i, 0] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.bufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.bufferOfImage.GetLength(1)) + phY)) + this.bufferOfImage[j, i].R;
                    valueOFImageWithNoise[j, i, 1] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.bufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.bufferOfImage.GetLength(1)) + phY)) + this.bufferOfImage[j, i].G;
                    valueOFImageWithNoise[j, i, 2] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.bufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.bufferOfImage.GetLength(1)) + phY)) + this.bufferOfImage[j, i].B;
                }
            }

            this.bufferOfImage = Normalize.Normalize11.Normalization(valueOFImageWithNoise);
            return BufferOfImage.Buffer.GetImage(this.bufferOfImage);
        }

        /// <summary>
        /// Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="numberOfPixelChange">The number of pixel change.</param>
        /// <param name="newValue">The new value of this pixel.</param>
        private void ChangeValueOfBuffer(int numberOfPixelChange, int newValue)
    {
        Random newRand = new Random();
           Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
        for (int i = 0; i < numberOfPixelChange; i++) 
            {
                Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                this.bufferOfImage[randomValue.X, randomValue.Y] = Color.FromArgb(newValue, newValue, newValue);
            }
    }

        /// <summary>
        /// Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="numberOfPixelChange">The number of pixel change.</param>
        /// <param name="a">start of uniform noise value.</param>
        /// <param name="b">end of uniform noise value.</param>
        private void ChangeValueOfBuffer(int numberOfPixelChange, int a, int b)
        {
            Random_Generate.RandomGenerate  = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            Random newRand = new Random();
            for (int j = a; j < b; j++)
            {
                for (int i = 0; i < numberOfPixelChange; i++)
                {
                    Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.bufferOfImage[randomValue.X, randomValue.Y].R + j;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.bufferOfImage[randomValue.X, randomValue.Y].G + j;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.bufferOfImage[randomValue.X, randomValue.Y].B + j;
                 }
            }

            for (int i = 0; i < (this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1)) - (numberOfPixelChange * (b - a)); i++)
            {
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 0] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 1] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 2] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].B;        
            }
        }

        /// <summary>
        ///  Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="mean">The mean of noise.</param>
        /// <param name="sigma">The sigma of noise.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        private void ChangeValueOfBuffer(double mean, double sigma, double precentage)
        {
            int allNumberOfPixelChange = 0;
            Random newRand = new Random();
            Random_Generate.RandomGenerate = new Random_Generate(this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.bufferOfImage.GetLength(0), this.bufferOfImage.GetLength(1), 3];
            for (int i = 0; i < 256; i++)
            {
                int numberOfPixel = (int)((1.0 / (Math.Sqrt(2 * Math.PI) * sigma)) * Math.Exp(-.5 * Math.Pow((i - mean) / sigma, 2)) * this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1) * precentage);
                for (int j = 0; j < numberOfPixel; j++) 
                {
                    Position randomValue = Random_Generate.RandomGenerate.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.bufferOfImage[randomValue.X, randomValue.Y].R + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.bufferOfImage[randomValue.X, randomValue.Y].G + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.bufferOfImage[randomValue.X, randomValue.Y].B + i;
                }

                allNumberOfPixelChange += numberOfPixel;
            }

            for (int i = 0; i < (this.bufferOfImage.GetLength(0) * this.bufferOfImage.GetLength(1)) - allNumberOfPixelChange; i++)
            {
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 0] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 1] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y, 2] = this.bufferOfImage[Random_Generate.RandomGenerate.RandArray[i].X, Random_Generate.RandomGenerate.RandArray[i].Y].B;
            }
        }

        /// <summary>
        /// Factioral the specified num.
        /// </summary>
        /// <param name="num">The num.</param>
        /// <returns>factial result</returns>
        private double Fact(double num)
        {
            if (num <= 1)
            {
                return 1;
            }
            else
            {
                return num * this.Fact(num - 1);
            }
        }
    }
}
